package org.apache.ibatis.executor.statement;

import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.sql.*;
import java.util.List;

/**
 * 底层依赖于java.sq1.PreparedStatement对象来完成数据库的相关操作
 * PreparedStatement是预编译的，使用PreparedStatement有几个好处:
 * 1.在执行可变参数的一条SQL时，PreparedStatement比Statement的效率高，因为DBMS预编译一条SQL当然会比多次编译一条SQL的效率要高;
 * 2.安全性好，有效防止Sql注入等问题;
 * 3.对于多次重复执行的语句，使用PreparedStatement效率会更高一点，并且在这种情况下也比较适合使用batch;
 * 4.代码的可读性和可维护性。
 *
 * @author Clinton Begin
 */
public class PreparedStatementHandler extends BaseStatementHandler {

    public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
        super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    }

    @Override
    public int update(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 执行 SQL
        ps.execute();
        // 返回受影响行数
        int rows = ps.getUpdateCount();
        // 获取用户传入的参数值，参数值类型可能是普通的实体类，也可能是 Map
        Object parameterObject = boundSql.getParameterObject();

        KeyGenerator keyGenerator = mappedStatement.getKeyGenerator();
        // 获取自增主键的值，并将值填入到参数对象中
        keyGenerator.processAfter(executor, mappedStatement, ps, parameterObject);
        return rows;
    }

    @Override
    public void batch(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.addBatch();
    }

    @Override
    public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        // 执行 SQL
        ps.execute();
        // 处理执行结果
        return resultSetHandler.<E>handleResultSets(ps);
    }

    @Override
    public <E> Cursor<E> queryCursor(Statement statement) throws SQLException {
        PreparedStatement ps = (PreparedStatement) statement;
        ps.execute();
        return resultSetHandler.<E>handleCursorResultSets(ps);
    }

    @Override
    protected Statement instantiateStatement(Connection connection) throws SQLException {
        String sql = boundSql.getSql();
        // 根据条件调用不同的 prepareStatement 方法创建 PreparedStatement
        if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
            String[] keyColumnNames = mappedStatement.getKeyColumns();
            if (keyColumnNames == null) {
                // 返回数据库生成的主键
                return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
            } else {
                // 在insert语句执行完成之后，会将keyColumnNames指定的列返回
                return connection.prepareStatement(sql, keyColumnNames);
            }
        } else if (mappedStatement.getResultSetType() != null) {
            // 设置结果集是否可以滚动以及其游标是否可以上下移动，设置结果集是否可更新
            return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
        } else {
            // 创建普通的PreparedStatement对象
            return connection.prepareStatement(sql);
        }
    }

    @Override
    public void parameterize(Statement statement) throws SQLException {
        // 通过参数处理器 ParameterHandler 设置运行时参数到 PreparedStatement 中
        parameterHandler.setParameters((PreparedStatement) statement);
    }

}
